﻿#include <hacks/net/tcp_acceptor.h>

namespace hacks::net {

    tcp_acceptor::tcp_acceptor(const shared_ev_loop &loop,
                               const ip_addr &addr) :
            loop_(loop),
            acceptor_fd_(nullptr),
            accept_func_(nullptr) {
        assert(loop);
        sa_family_t network_family = addr.get_domain();
#if defined(VALGRIND)
        ev_os_sock_t new_sock_fd = os_sock_new(network_family, SOCK_STREAM , IPPROTO_TCP);
#elif defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
        ev_os_sock_t new_sock_fd = os_sock_new(network_family,
                                               SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
                                               IPPROTO_TCP);
#endif
        assert(new_sock_fd != INVALID_SOCKET);
        socket_ops ops(new_sock_fd);
#if defined(VALGRIND)
        ops.set_nonblock();
        ops.set_cloexec();
#endif
        ops.set_reuseaddr(true);
        ops.set_reuseport(true);
        acceptor_fd_ = mk_ev_sock_fd(loop, new_sock_fd,
                                     std::bind(&tcp_acceptor::on_acceptor_event,
                                               this,
                                               std::placeholders::_1,
                                               std::placeholders::_2,
                                               std::placeholders::_3));
        acceptor_fd_->set_userdata(this);
        if (network_family == AF_INET) {
            ops.bindv4(dynamic_cast<const ip4_addr &>(addr));
        } else if (network_family == AF_INET6) {
            ops.bindv6(dynamic_cast<const ip6_addr &>(addr));
        } else {
            //err , unrecognized network family
            assert(false);
        }
        loop_->run_in_loop([this, ops]() mutable {
            ops.listen();
            ops.shutdown_write();
            acceptor_fd_->commit();
            acceptor_fd_->update_event(ev_event_readable);
        });
    }

    void tcp_acceptor::on_acceptor_event(ev_sock_fd &fd, int status, int evt) {
        if (!evt & ev_event_readable) {
            return;
        }
        socket_ops ops(acceptor_fd_->get_os_fd());
        auto incoming_fd = ops.do_accept();
        if (incoming_fd < 0) {
            //todo fucking error! fix me!
            return;
        }
        if (accept_func_) {
            accept_func_(incoming_fd);
        } else {
            ops.close();
        }
    }

    void tcp_acceptor::set_accept_callback(acceptor_functor_t &&f) {
        accept_func_ = std::move(f);
    }

    shared_ev_loop tcp_acceptor::get_loop() const {
        return loop_;
    }
}
